namespace ScrumSprintMonitor.Implementation
{
	#region #using Directives

	using System;
	using System.ComponentModel;
	using System.Diagnostics;

	using Infrastructure;

	#endregion

	[DebuggerDisplay("TeamMemberInformation: {Name}, alias={displayAlias}, {timeZone}")]
	public class TeamMemberInformation : ITeamMemberInformation
	{
		public TeamMemberInformation(string name, bool unassigned, string displayAlias, string schedulerAlias, TimeZoneInfo timeZone, DateTime workdayStart,
		                             DateTime workdayEnd, DateTime lunchHourStart, DateTime lunchHourEnd, double hourlyProductivityFactor,
		                             bool dedicatedToSprint)
		{
			Debug.Assert(workdayStart.Kind == DateTimeKind.Unspecified);
			Debug.Assert(workdayEnd.Kind == DateTimeKind.Unspecified);
			Debug.Assert(lunchHourStart.Kind == DateTimeKind.Unspecified);
			Debug.Assert(lunchHourEnd.Kind == DateTimeKind.Unspecified);

			this.name = name;
			this.dedicatedToSprint = dedicatedToSprint;
			this.displayAlias = displayAlias;
			this.hourlyProductivityFactor = hourlyProductivityFactor;
			this.unassigned = unassigned;
			this.schedulerAlias = schedulerAlias;
			this.timeZone = timeZone;

			this.workdayStart = workdayStart.TimeOfDay;
			this.workdayEnd = workdayEnd.TimeOfDay;
			if (this.workdayEnd < this.workdayStart)
			{
				this.workdayEnd = this.workdayEnd.Add(TimeSpan.FromDays(1));
			}
			this.lunchHourStart = lunchHourStart.TimeOfDay;
			this.lunchHourEnd = lunchHourEnd.TimeOfDay;
			if (this.lunchHourEnd < this.lunchHourStart)
			{
				this.lunchHourEnd = this.lunchHourEnd.Add(TimeSpan.FromDays(1));
			}
		}

		#region Implementation of ITeamMemberInformation

		private readonly bool dedicatedToSprint;
		private readonly string displayAlias;
		private readonly double hourlyProductivityFactor;
		private readonly TimeSpan lunchHourEnd, lunchHourStart;
		private readonly string name;
		private readonly string schedulerAlias;
		private readonly TimeZoneInfo timeZone;
		private readonly bool unassigned;
		private readonly TimeSpan workdayEnd, workdayStart;
		private IAvatar avatar;

		public string Name
		{
			get { return this.name; }
		}

		public string DisplayAlias
		{
			get { return this.displayAlias; }
		}

		public IAvatar Avatar
		{
			get { return this.avatar; }
			set
			{
				if (value != this.avatar)
				{
					this.avatar = value;

					OnPropertyChanged("Avatar");
				}
			}
		}

		public string SchedulerAlias
		{
			get { return this.schedulerAlias; }
		}

		public TimeZoneInfo TimeZone
		{
			get { return this.timeZone; }
		}

		public double HourlyProductivityFactor
		{
			get { return this.hourlyProductivityFactor; }
		}

		public bool DedicatedToSprint
		{
			get { return this.dedicatedToSprint; }
		}

		/// <summary>
		/// Gets the start DateTime of the day in date.
		/// </summary>
		/// <param name="date">A date expressed in the the team member's timezone.</param>
		/// <returns>A date expressed in the the team member's timezone that points to the start of that workday.</returns>
		public DateTime GetWorkdayStart(DateTime date)
		{
			return date.Date.Add(this.workdayStart);
		}

		/// <summary>
		/// Gets the end DateTime of the day in date.
		/// </summary>
		/// <param name="date">A date expressed in the the team member's timezone.</param>
		/// <returns>A date expressed in the the team member's timezone that points to the end of that workday.</returns>
		public DateTime GetWorkdayEnd(DateTime date)
		{
			return date.Date.Add(this.workdayEnd);
		}

		/// <summary>
		/// Determines whether the team member is busy in a specific time slot.
		/// </summary>
		/// <param name="time">A timestamp expressed in the the team member's timezone.</param>
		/// <param name="slotSize">The size of the slot to check.</param>
		/// <returns>Whether the team member is busy.</returns>
		public bool IsBusy(DateTime time, TimeSpan slotSize)
		{
			return (time.TimeOfDay >= this.lunchHourStart && time.TimeOfDay < this.lunchHourEnd);
		}

		#endregion

		#region INotifyPropertyChanged members

		public event PropertyChangedEventHandler PropertyChanged;

		public void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		#endregion
	}
}